Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

494
Visualizações
In Ruby, in a method defined in class << self, why can't a constant defined on the superclass be access without self?

I'm trying to understand Ruby singletons and class inheritance better. I read everywhere that

def self.method_name; end`

is equivalent to

class << self
  def method_name; end
end

But if that were true, then I would expect print_constant_fails to work, but it doesn't. What is going on here?

class SuperExample
  A_CONSTANT = "super example constant"
end

class SubExample < SuperExample
  def self.print_constant_works_1
    puts A_CONSTANT
  end
  class << self
    def print_constant_works_2
      puts self::A_CONSTANT
    end
    def print_constant_fails
      puts A_CONSTANT
    end
  end
end
pry(main)> SubExample.print_constant_works_1
super example constant

pry(main)> SubExample.print_constant_works_2
super example constant

pry(main)> SubExample.print_constant_fails
NameError: uninitialized constant #<Class:SubExample>::A_CONSTANT
from (pry):13:in `print_constant_fails'
over 4 years ago · Santiago Trujillo
3 Respostas
Responde à pergunta

0

It has to do with scope. When you are inside class << self, the scope is different than when you are inside class Something. Thus, inside class << self there is actually no constant called A_CONSTANT.

over 4 years ago · Santiago Trujillo Relatório

0

You have encountered a common Ruby gotcha - constant lookup.

The most important concept in constant lookup is Module.nesting (unlike in method lookup, where the primary starting point is self). This method gives you the current module nesting which is directly used by the Ruby interpreter when resolving the constant token. The only way to modify the nesting is to use keywords class and module and it only includes modules and classes for which you used that keyword:

class A
  Module.nesting #=> [A]

  class B
    Module.nesting #=> [A::B, A]
  end
end

class A::B
  Module.nesting #=> [A::B] sic! no A
end

In meta programming, a module or class can be defined dynamically using Class.new or Module.new - this does not affect nesting and is an extremely common cause of bugs (ah, also worth mentioning - constants are defined on the first module of Module.nesting):

module A
  B = Class.new do
    VALUE = 1
  end

  C = Class.new do
    VALUE = 2
  end
end

A::B::VALUE #=> uninitialized constant A::B::VALUE
A::VALUE #=> 2

The above code will generate two warnings: one for double initialization of constant A::VALUE and a second for reassigning the constant.

If it looks like "I'd never do that" - this also applies to all the constants defined within RSpec.describe (which internally calls Class.new), so if you define a constant within your rspec tests, they are most certainly global (unless you explicitly stated the module it is to be defined in with self::)

Now let's get back to your code:

class SubExample < SuperExample
  puts Module.nesting.inspect #=> [SubExample]

  class << self
    puts Module.nesting.inspect #=> [#<Class:SubExample>, SubExample]
  end
end

When resolving the constant, the interpreter first iterates over all the modules in Module.nesting and searches this constant within that module. So if nesting is [A::B, A] and we're looking for the constant with token C, the interpreter will look for A::B::C first and then A::C.

However, in your example, that will fail in both cases :). Then the interpreter starts searching ancestors of the first (and only first) module in Module.nesting. SubrExample.singleton_class.ancestors gives you:

[
  #<Class:SubExample>,
  #<Class:SuperExample>,
  #<Class:Object>,
  #<Class:BasicObject>,
  Class,
  Module,
  Object,
  Kernel,
  BasicObject
]

As you can see - there is no SuperExample module, only its singleton class - which is why constant lookup within class << self fails (print_constant_fails).

The ancestors of Subclass are:

[
  SubExample,
  SuperExample,
  Object,
  Kernel,
  BasicObject
]

We have SuperExample there, so the interpreter will manage to find SuperExample::A_CONSTANT within this nesting.

We're left with print_constant_works_2. This is an instance method on a singleton class, so self within this method is just SubExample. So, we're looking for SubExample::A_CONSTANT - constant lookup firstly searches on SubExample and, when that fails, on all its ancestors, including SuperExample.

over 4 years ago · Santiago Trujillo Relatório

0

In Ruby, every Ruby's constant has its own path, start from the main (root) with the sign :: (default, we don't need to declare this sign). And class should not be considered a keyword (kind of static), but a method (kind of dynamic) take responsibility for creating a class object and a class name constant which point to that class object, and all Constants are defined inside a class without a path (P::Q::...) will automatically be considered belongs to the created class with path :: ClassName::A_CONSTANT.

GLOBAL = 1
class SuperExample
  A_CONSTANT = "super constant" # <-- ::SuperExample::A_CONSTANT
end

puts ::GLOBAL # 1
puts ::SuperExample # SuperExample
puts ::SuperExample::A_CONSTANT # "super constant"

It looks like constants paths in children classes has same level with parent

class SubExample < SuperExample
end
puts ::SubExample::A_CONSTANT # "super constant"

As I noticed, all constants (without ::) inside the class block will be set path under the classpath, so when you get them, either you get with the explicitly constant path or under the class that constants belong to:

class SubExample < SuperExample
 def self.print_constant_works_1
  puts A_CONSTANT # ::SubExample::A_CONSTANT
 end

 def another
  puts A_CONSTANT # ::SubExample::A_CONSTANT
 end

 def yet_another
  puts SubExample::A_CONSTANT # ::SubExample::A_CONSTANT
 end
end

Now check class << self

class SubExample < SuperExample
 class << self
  puts self # <Class:SubExample>

  def print_constant_works_2
   puts self::A_CONSTANT # declare explicitly constant path
  end

  def print_constant_fails
   puts A_CONSTANT # not declare explicitly <-- Class:SubExample::A_CONSTANT
  end
 end
end

As you can see, the class inside class << self is different, so the path of constant A_CONSTANT inside method print_constant_fails is pointing to Class:SubExample which does not define any constant A_CONSTANT, so an error uninitialized constant #<Class:SubExample>::A_CONSTANT be raised.

Meanwhile print_constant_works_2 will work since we declare explicitly constant path, and self in this case is actually SubExample(call SubExample.print_constant_works_2).

Now let try with an explicit path ::A_CONSTANT inside print_constant_fails

def print_constant_fails
 puts ::A_CONSTANT
end

The error be raised is uninitialized constant A_CONSTANT, ::A_CONSTANT is considered a global constant (main).

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda